package com.tjc.ratelimit;

import com.github.benmanes.caffeine.cache.CacheLoader;
import com.github.benmanes.caffeine.cache.Caffeine;
import com.github.benmanes.caffeine.cache.Expiry;
import com.github.benmanes.caffeine.cache.LoadingCache;
import org.checkerframework.checker.index.qual.NonNegative;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;

import java.util.concurrent.TimeUnit;

/**
 * @Author: tjc
 * @description
 * @Date Created in 23:59 2022-02-20
 */
class FixedWindowPolicy implements RateLimitPolicy {

    private int limit;
    private long expireMs;
    LoadingCache<String, Integer> cache;

    public FixedWindowPolicy(int limit, long expireMs) {
        this.limit = limit;
        this.expireMs = expireMs;
        cache = Caffeine
                .newBuilder()
                .initialCapacity(1)
                .maximumSize(100)
                .expireAfter(new Expiry<String, Integer>() {
                    @Override
                    public long expireAfterCreate(@NonNull String s, @NonNull Integer integer, long l) {
                        return TimeUnit.MILLISECONDS.toNanos(expireMs);
                    }

                    @Override
                    public long expireAfterUpdate(@NonNull String s, @NonNull Integer integer, long l, @NonNegative long l1) {
                        return l1;
                    }

                    @Override
                    public long expireAfterRead(@NonNull String s, @NonNull Integer integer, long l, @NonNegative long l1) {
                        return l1;
                    }
                }).build(new CacheLoader<String, Integer>() {
                    @Override
                    public @Nullable Integer load(@NonNull String s) throws Exception {
                        return -1;
                    }
                });
    }

    @Override
    public synchronized boolean access(String serviceName) {
        /**
         * 1. if contains key
         *      value > limit -> return false
         *    else
         *      value++;
         * 2. not exists key
         *    create key expired
         */
        Integer count;
        if ((count = cache.get(serviceName)) != -1) {
            if (count >= limit) {
                return false;
            } else {
                cache.put(serviceName, count + 1);
                return true;
            }
        }
        cache.put(serviceName, 1);
        return true;
    }
}
